package utils

import (
	"bytes"
	"database/sql"
	_ "github.com/lib/pq"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	"os"
	"strings"
	"text/template"
)

// GenCode 生成代码主方法
func GenCode(tableName, packName, className, structName, fileName, headName string) {
	sqlStr := "SELECT a.attname AS col_name, a.attname AS field_name, t.typname AS data_type, d.description AS col_remarks, " +
		"SUBSTRING(format_type(a.atttypid, a.atttypmod) FROM '\\(.*\\)') AS data_length " +
		"FROM pg_attribute a " +
		"LEFT JOIN pg_description d ON d.objoid = a.attrelid AND d.objsubid = a.attnum " +
		"LEFT JOIN pg_class c ON a.attrelid = c.oid " +
		"LEFT JOIN pg_type t ON a.atttypid = t.oid " +
		"WHERE a.attnum >= 0 AND c.relname LIKE '" + tableName + "' " +
		"ORDER BY c.relname DESC, a.attnum ASC "
	res := GetAll(sqlStr)
	if res.Code == Succeed.Code {
		dataMap := make(map[string]any)
		dataMap["PackName"] = packName
		dataMap["TableName"] = tableName
		dataMap["ClassName"] = className
		dataMap["StructName"] = structName
		dataMap["FileName"] = fileName
		dataMap["HeadName"] = headName
		dataMap["Columns"] = res.Data
		var tmplBytes bytes.Buffer

		// 生成Model代码
		tmplFile, err := template.ParseFiles("res/tmpl/model.template")
		if err != nil {
			panic("生成Model的模板文件不存在或读取模板文件有误，请检查～！" + err.Error())
		}
		tmplBytes = bytes.Buffer{}
		err = tmplFile.Execute(&tmplBytes, dataMap)
		createFile(tmplBytes, "out/"+fileName+"-mod.go")

		// 生成Service代码
		tmplFile, err = template.ParseFiles("res/tmpl/service.template")
		if err != nil {
			panic("生成Service的模板文件不存在或读取模板文件有误，请检查～！" + err.Error())
		}
		tmplBytes = bytes.Buffer{}
		err = tmplFile.Execute(&tmplBytes, dataMap)
		createFile(tmplBytes, "out/"+fileName+"-serv.go")

		// 生成Controller代码
		tmplFile, err = template.ParseFiles("res/tmpl/controller.template")
		if err != nil {
			panic("生成Controller的模板文件不存在或读取模板文件有误，请检查～！" + err.Error())
		}
		tmplBytes = bytes.Buffer{}
		err = tmplFile.Execute(&tmplBytes, dataMap)
		createFile(tmplBytes, "out/"+fileName+"-ctrl.go")

		// 生成Vue代码
		tmplFile, err = template.ParseFiles("res/tmpl/vue.template")
		if err != nil {
			panic("生成Vue的模板文件不存在或读取模板文件有误，请检查～！" + err.Error())
		}
		tmplBytes = bytes.Buffer{}
		err = tmplFile.Execute(&tmplBytes, dataMap)
		createFile(tmplBytes, "out/"+className+".vue")
	} else {
		panic(res.Msg)
	}
}

// createFile 创建文件并且将模板数据写入到新文件中
func createFile(content bytes.Buffer, fileName string) {
	file, err := os.Create(fileName)
	if err != nil {
		panic("文件创建失败，请检查～！" + err.Error())
	}
	_, err = file.WriteString(content.String())
	if err != nil {
		panic("代码写入文件失败，请检查～！" + err.Error())
	}
}

var Conn *sql.DB

// initPgsql 连接MySQL数据库
func initPgsql() {
	var err error
	dataSrcName := "host=39.101.203.102 port=5432 user=postgres password=01235689!Hh dbname=ubt_db_admin sslmode=disable"
	if Conn, err = sql.Open("postgres", dataSrcName); err != nil {
		panic("数据库连接失败。" + err.Error())
	}
	if err = Conn.Ping(); err != nil {
		panic("数据库Ping连接失败。" + err.Error())
	}
}

// GetAll 获取多行记录
func GetAll(sqlStr string, args ...any) *ResJson {
	initPgsql()
	// 查询数据
	rows, err := Conn.Query(sqlStr, args...)
	// 最终关闭数据库连接
	defer func(rows *sql.Rows) {
		if err = rows.Close(); err != nil {
			panic("关闭数据查询失败～！" + err.Error())
			return
		}
	}(rows)
	if err == nil {
		result := make([]map[string]any, 0)
		var (
			dbMap map[string]any
			flag  uint8
		)
		for rows.Next() {
			if dbMap = scanRow(rows); dbMap != nil {
				result = append(result, dbMap)
			} else {
				flag = 1
				break
			}
		}
		if flag == 0 {
			return &ResJson{Code: Succeed.Code, Msg: Succeed.Msg, Data: result}
		}
	}
	panic("数据库查询失败～！" + err.Error())
	return PackErr
}

// scanRow 将数据库字段转换为Map
func scanRow(rows *sql.Rows) map[string]any {
	if columns, err := rows.Columns(); err == nil {
		vals := make([]any, len(columns))
		valsPtr := make([]any, len(columns))
		for i := range vals {
			valsPtr[i] = &vals[i]
		}
		if err = rows.Scan(valsPtr...); err == nil {
			r := make(map[string]any)
			c := cases.Title(language.Dutch)
			for i, v := range columns {
				// 将数据表中各字段下划线转成驼峰
				v = strings.Replace(v, "_", " ", -1)
				v = c.String(v)
				v = strings.Replace(v, " ", "", -1)
				v = strings.ToLower(string(v[0])) + v[1:]
				if va, ok := vals[i].([]byte); ok {
					r[v] = string(va)
				} else {
					r[v] = vals[i]
				}
				if v == "fieldName" {
					val := r[v].(string)
					val = strings.Replace(val, "_", " ", -1)
					val = c.String(val)
					val = strings.Replace(val, " ", "", -1)
					r[v] = val
				}
			}
			return r
		}
		panic("数据字段转换失败～！" + err.Error())
	}
	return nil
}
